home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 2
/
Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso
/
Aminet
/
comm
/
misc
/
xprzedzap090.lzh
/
Receive.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-11-30
|
21KB
|
636 lines
/**********************************************************************
* Receive.c: File reception routines for xprzmodem.library;
* Version 2.10, 12 February 1991, by Rick Huebner.
* Based closely on Chuck Forsberg's rz.c example ZModem code,
* but too pervasively modified to even think of detailing the changes.
* Released to the Public Domain; do as you like with this code.
*
* Version 2.50, 15 November 1991, CRC-32 additions by William M. Perkins.
**********************************************************************
* Modified to support ZedZap/ZedZip fidonet protocol by
* Yves Konighofer and Russel McOrmond.
*
* Adapted to xprzmodem Version 2.52 by Andrea Cisternino (30 Nov 92)
**********************************************************************/
#include <proto/all.h>
#include <exec/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xproto.h"
#include "zmodem.h"
#include "xprzmodem.h"
#ifdef DEBUGLOG
extern void *DebugLog;
#endif
/**********************************************************
* long XProtocolReceive(struct XPR_IO *xio)
*
* Main file reception routine; called by comm program
**********************************************************/
long __saveds __asm XProtocolReceive(register __a0 struct XPR_IO *xio)
{
struct SetupVars *sv;
struct Vars *v;
UBYTE err = FALSE;
/* Perform common setup and initializations */
if (! (v = setup(xio))) return XPRS_FAILURE;
v->Tryzhdrtype = ZRINIT;
v->Rxtimeout = 100;
sv = (void *) v->io.xpr_data;
if (sv->bufpos)
{
v->Modemchar = v->Modembuf;
if (sv->buflen > sizeof(v->Modembuf)) sv->buflen = sizeof(v->Modembuf);
memcpy(v->Modembuf, sv->bufpos,sv->buflen);
v->Modemcount = sv->buflen;
}
/* first clear the inbound buffer */
if (v->io.xpr_sflush) (*v->io.xpr_sflush)();
/* Transfer the files */
if (rcvbatch(v) == ERROR)
{
upderr(v, "Download cancelled or timed out");
err = TRUE;
}
else updmsg(v, "Done.");
/* Clean up and return */
if (v->io.xpr_setserial && v->Oldstatus != -1) (*v->io.xpr_setserial)(v->Oldstatus);
FreeMem(v->Filebuf, v->Filebufmax);
FreeMem(v, (long) sizeof(struct Vars));
#ifdef DEBUGLOG
if (DebugLog)
{
xpr_fclose(&v->io, DebugLog);
DebugLog = NULL;
}
#endif
return (err) ? XPRS_FAILURE : XPRS_SUCCESS;
} /* End of long XProtocolReceive() */
/**********************************************************
* short rcvbatch(struct Vars *v)
*
* Start the batch transfer
**********************************************************/
short rcvbatch(struct Vars *v)
{
switch (tryz(v))
{
case ZCOMPL:
return OK;
case ZFILE:
if (rzfiles(v) == OK) return OK;
}
/* Do we really want to just can it? */
canit(v);
return ERROR;
} /* End of short rcvbatch() */
/**********************************************************
* short tryz(struct Vars *v)
*
* Negotiate with sender to start a file transfer
**********************************************************/
short tryz(struct Vars *v)
{
short n, errors = 0;
for (n = v->ErrorLimit; --n >= 0; )
{
/* Set max frame length and capability flags */
stohdr(v, (long) v->Tframlen);
v->Txhdr[ZF0] = CANFC32 | CANFDX | CANOVIO;
zshhdr(v, v->Tryzhdrtype);
sendbuf(v);
again:
/* Check for abort from comm program */
if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)()) return ERROR;
switch (zgethdr(v))
{
case ZFILE: /* File name and info packet */
v->Zconv = v->Rxhdr[ZF0]; /* Suggested txt mode; ZCNL = text, */
/* ZCBIN = binary, 0 = don't know. */
v->Zmanag = v->Rxhdr[ZF1]; /* Suggested file management mode. */
v->Ztrans = v->Rxhdr[ZF2]; /* Suggested file transport mode. */
v->Tryzhdrtype = ZRINIT;
if (zrdata(v, v->Pktbuf, KSIZE) == GOTCRCW) return ZFILE;
zshhdr(v, ZNAK); /* Packet mangled, ask for retry */
sendbuf(v);
if (--n < 0)
{
upderr(v,"Unable to Get A Valid Header (ZFILE)");
return ERROR;
}
goto again;
case ZSINIT: /* Special attention-grabbing string to use to */
/* interrupt sender */
if (zrdata(v, v->Attn, ZATTNLEN) == GOTCRCW) zshhdr(v, ZACK);
else zshhdr(v, ZNAK);
sendbuf(v);
if (--n < 0)
{
upderr(v,"Unable to Get A Valid Header (ZFILE)");
return ERROR;
}
goto again;
case ZFREECNT: /* Sender wants to know how much room we've got */
stohdr(v, getfree());
zshhdr(v, ZACK);
sendbuf(v);
goto again;
case ZCOMMAND: /* Sender wants us to do remote commands, */
/* but we don't do requests. */
if (zrdata(v, v->Pktbuf, KSIZE) == GOTCRCW)
{
mysprintf(v->Msgbuf, "Ignoring command: %s", v->Pktbuf);
upderr(v, v->Msgbuf); /* Ignore and report all uploaded commands */
stohdr(v, 0L); /* whilst telling sender they worked; */
do
{
zshhdr(v, ZCOMPL); /* paranoia can be good for you... */
sendbuf(v);
} while (++errors < v->ErrorLimit && zgethdr(v) != ZFIN);
ackbibi(v);
return ZCOMPL;
}
else zshhdr(v, ZNAK);
sendbuf(v);
if (--n < 0)
{
upderr(v,"Unable to Get A Valid Header (ZFILE)");
return ERROR;
}
goto again;
case ZCOMPL:
goto again;
case ZFIN: /* Sender has ended batch */
ackbibi(v);
return ZCOMPL;
case ZCAN:
upderr(v,"Transfer Cancelled (Remote Sent ZCAN)");
return ERROR;
case RCDO:
upderr(v,"Transfer Cancelled (Remote Dropped Carrier)");
return ERROR;
} /* End of "switch (zgethdr(v))" */
} /* End of "for(n = v->ErrorLimit; --n >= 0; )" */
return ERROR;
} /* End of short tryz() */
/**********************************************************
* short rzfiles(struct Vars *v)
*
* Receive a batch of files
**********************************************************/
short rzfiles(struct Vars *v)
{
struct SetupVars *sv;
short c;
/* Keep receiving files until end of batch or error */
while (TRUE)
{
v->Filename[0]='\0';
switch (c = rzfile(v))
{
case ZSKIP:
case ZEOF:
if(strlen(v->Filename)) updstatus(v,v->Filename,XPRS_SUCCESS);
switch (tryz(v))
{
case ZCOMPL:
return OK;
default:
return ERROR;
case ZFILE:
break;
}
break;
default:
if (c == ERROR) updmsg(v, "34-File Transfer FAILED");
bfclose(v);
/* Tell the system it failed! */
if(strlen(v->Filename)) updstatus(v,v->Filename,XPRS_FAILURE);
sv = (void *) v->io.xpr_data;
if (*sv->option_k == 'N' && v->io.xpr_extension >= 2 && v->io.xpr_unlink)
{
updmsg(v, "41-Deleting partially received file");
(*v->io.xpr_unlink)(v->Filename);
}
else updmsg(v, "Keeping partially received file");
return c;
}
}
} /* End of short rzfiles() */
/**********************************************************
* short rzfile(struct Vars *v)
*
* Receive one file; file name packet already read into
* Pktbuf by tryz()
**********************************************************/
short rzfile(struct Vars *v)
{
short c, n;
/*
* Process file name packet; either open file and prepare to receive,
* or tell us to skip this one. File is now opened!
*/
c = procheader(v);
if (c == ERROR || c == ZSKIP) return v->Tryzhdrtype = ZSKIP;
n = v->ErrorLimit;
v->Rxbytes = v->Strtpos;
v->Rxcount = 0;
v->Eofseen = FALSE;
/* Receive ZDATA frames until finished */
while (TRUE)
{
stohdr(v, v->Rxbytes); /* Tell sender where to start frame */
zshhdr(v, ZRPOS);
sendbuf(v);
nxthdr:
/* Check for abort from comm program */
if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)()) return ERROR;
switch (c = zgethdr(v)) /* Wait for frame header */
{
case ZDATA: /* More file data packets forthcoming */
if ((v->Rxpos > v->Rxbytes) || ((v->Rxpos + 2*(v->Rxcount)) < v->Rxbytes))
/* We aren't in sync; go back */
{
if ( --n < 0) return ERROR;
v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
mysprintf(v->Msgbuf, "Data at bad position; here=%ld, there=%ld", v->Rxbytes, v->Rxpos);
v->xpru.xpru_errormsg = (char *) v->Msgbuf;
++v->xpru.xpru_errors;
(*v->io.xpr_update)(&v->xpru);
zmputs(v, v->Attn);
continue;
}
/* Receive file data packet(s) */
moredata:
/* Give comm program its timeslice if it needs one */
if (v->io.xpr_chkmisc) (*v->io.xpr_chkmisc)();
/* Check for abort from comm program */
if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)())
{
upderr(v,"Transfer Cancelled (Local Abort)");
return ERROR;
}
switch (c = zrdata(v, v->Pktbuf,KSIZE))
{
case ZCAN:
upderr(v,"Transfer Cancelled (Remote Sent ZCAN)");
return ERROR;
case RCDO:
upderr(v,"Transfer cancelled (Remote Dropped Carrier)");
return ERROR;
case ERROR: /* CRC error or packet too long */
if ( --n < 0) return ERROR;
v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
mysprintf(strchr(v->Msgbuf, '\0'), "@ %ld; %ld retries left", v->Rxbytes, (long) n);
v->xpru.xpru_errormsg = (char *) v->Msgbuf;
++v->xpru.xpru_errors;
(*v->io.xpr_update)(&v->xpru);
zmputs(v, v->Attn);
continue;
case TIMEOUT:
if ( --n < 0) return ERROR;
v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
mysprintf(strchr(v->Msgbuf, '\0'), "@ %ld; %ld retries left", v->Rxbytes, (long) n);
v->xpru.xpru_errormsg = (char *) v->Msgbuf;
++v->xpru.xpru_timeouts;
(*v->io.xpr_update)(&v->xpru);
continue;
case GOTCRCW: /* Sender says it's waiting for an ACK */
n = v->ErrorLimit;
if (putsec(v) == ERROR) return ERROR;
stohdr(v, v->Rxbytes);
zshhdr(v, ZACK);
sendbuf(v);
goto nxthdr;
case GOTCRCQ: /* Sender says it's not waiting, */
/* but ACK anyway (rarely used) */
n = v->ErrorLimit;
if (putsec(v) == ERROR) return ERROR;
stohdr(v, v->Rxbytes);
zshhdr(v, ZACK);
sendbuf(v);
goto moredata;
case GOTCRCG: /* Sender says keep receiving, */
/* there's more coming */
n = v->ErrorLimit;
if (putsec(v) == ERROR) return ERROR;
goto moredata;
case GOTCRCE: /* Sender says this is the last packet */
n = v->ErrorLimit;
if (putsec(v) == ERROR) return ERROR;
goto nxthdr;
} /* End of "switch (c = zrdata(v, v->Pktbuf,KSIZE))" */
case ZNAK:
case TIMEOUT:
if (--n < 0) return ERROR;
v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
mysprintf(strchr(v->Msgbuf, '\0'), "@ %ld; %ld retries left",v->Rxbytes, (long) n);
v->xpru.xpru_errormsg = (char *) v->Msgbuf;
++v->xpru.xpru_timeouts;
(*v->io.xpr_update)(&v->xpru);
continue;
case ZFILE:
zrdata(v, v->Pktbuf, KSIZE); /* Read and discard redundant */
continue; /* filename packet */
case ZEOF:
if (v->Rxpos != v->Rxbytes) /* We aren't in sync; go back */
{
mysprintf(v->Msgbuf, "Bad EOF; here=%ld, there=%ld",v->Rxbytes, v->Rxpos);
upderr(v, v->Msgbuf);
continue;
}
bfclose(v); /* All done; close file */
updmsg(v,"33-File Transfer OK");
updmsg(v,"28-Checking For Next File");
return c;
case ERROR: /* Too much garbage while waiting for frame header */
if ( --n < 0) return ERROR;
v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
mysprintf(strchr(v->Msgbuf, '\0'), "@ %ld; %ld retries left",v->Rxbytes, (long) n);
v->xpru.xpru_errormsg = (char *) v->Msgbuf;
++v->xpru.xpru_errors;
(*v->io.xpr_update)(&v->xpru);
zmputs(v, v->Attn);
continue;
case ZSKIP:
return c;
default:
return ERROR;
} /* End of "switch (c = zgethdr(v))" */
} /* End of "while (TRUE)" */
} /* End of short rzfile() */
/**********************************************************
* short procheader(struct Vars *v)
*
* Process file name & info packet; either open file and
* prepare to receive, or return ERROR if we should skip
* this one for some reason
**********************************************************/
short procheader(struct Vars *v)
{
struct SetupVars *sv;
UBYTE *p, *openmode, buff[PATHLEN];
long n;
openmode = "w";
v->Strtpos = 0;
/* Extract expected filesize from file info packet, if given */
v->Fsize = -1;
p = strchr(v->Pktbuf, '\0') + 1;
if (*p) v->Fsize = atol(p);
/*
* Make sure we have room for file; skip it if not.
* Commented out for now, since getfree() isn't implemented yet.
if (v->Fsize > getfree())
{
mysprintf(v->Msgbuf, "Insufficient disk space; need %ld bytes, have %ld", v->Fsize, getfree());
upderr(v, v->Msgbuf);
v->Noroom = TRUE;
return ERROR;
}
*/
/* If option RY set, use full received file path */
sv = (void *) v->io.xpr_data;
if (*sv->option_r == 'Y') strcpy(v->Filename, v->Pktbuf);
else
{
/* else use the default directory path specified in the setup options */
strcpy(v->Filename, sv->option_p);
p = v->Filename + strlen(v->Filename) - 1;
if (p >= v->Filename && *p != '/' && *p != ':') *++p = '/';
*++p = '\0';
/*
* Append the filename from the file info packet; ignore anything before
* last /, \, or : in filename (received directory path is ignored)
*/
p = strchr(v->Pktbuf, '\0'); /* start at end and scan back */
/* to start of name */
while (p >= v->Pktbuf && *p != '/' && *p != '\\' && *p != ':') --p;
strcat(v->Filename, ++p);
}
/* Display name of file being received for user */
v->xpru.xpru_updatemask = XPRU_FILENAME;
v->xpru.xpru_filename = (char *) v->Filename;
(*v->io.xpr_update)(&v->xpru);
/*
* If a file with this name already exists, handle in
* accordance with O option
*/
if (exist(v))
{
switch (*sv->option_o)
{
case 'N': /* Don't overwrite; change name to prevent collision */
strcpy(buff, v->Filename);
strcat(v->Filename, ".dup");
n = 2;
while (exist(v))
{
mysprintf(v->Filename, "%s.dup%ld", buff, n);
++n;
}
/* Update filename display to show new name */
(*v->io.xpr_update)(&v->xpru);
break;
case 'R': /* Resume transfer from current end of file */
openmode = "a";
v->Strtpos = (*v->io.xpr_finfo)(v->Filename, 1L);
break;
case 'S': /* Skip it */
upderr(v, "File already exists; skipping");
return ZSKIP;
/* Else 'Y', go ahead and overwrite it (openmode = w) */
}
}
/* Set text/binary mode according to options before opening file */
set_textmode(v);
/*
* Figure out file translation mode to use; either binary (verbatim
* transfer) or ASCII (perform end-of-line conversions). If user has
* specified a mode (TY or TN), that's what we use. If user says use
* sender's suggestion (T?), set mode according to Zconv flag. If neither
* side specifies, default to binary mode.
*/
v->Thisbinary = v->Rxbinary || !v->Rxascii;
if (! v->Rxbinary && v->Zconv == ZCNL) v->Thisbinary = FALSE;
if (! v->Rxascii && v->Zconv == ZCBIN) v->Thisbinary = TRUE;
/* Open the file (finally) */
if (!bfopen(v, openmode))
{
++v->Errcnt;
upderr(v, "Can't open file; skipping");
return ERROR;
}
getsystime(&v->Starttime);
/* Initialize comm program transfer status display */
v->xpru.xpru_updatemask = XPRU_PROTOCOL | XPRU_FILESIZE | XPRU_MSG
| XPRU_BLOCKS | XPRU_ERRORS | XPRU_TIMEOUTS | XPRU_BLOCKCHECK
| XPRU_BYTES | XPRU_EXPECTTIME | XPRU_ELAPSEDTIME| XPRU_DATARATE;
v->xpru.xpru_protocol = "ZedZap";
v->xpru.xpru_filesize = v->Fsize;
v->xpru.xpru_msg = (v->Thisbinary) ? "Receiving binary file..." : "Receiving text file...";
v->xpru.xpru_blocks = v->xpru.xpru_errors = v->xpru.xpru_timeouts = 0;
v->xpru.xpru_blockcheck = v->Crc32 ? "CRC-32" : "CRC-16";
v->xpru.xpru_bytes = v->Strtpos;
update_rate(v);
(*v->io.xpr_update)(&v->xpru);
return OK;
} /* End of short procheader() */
/**********************************************************
* short putsec(struct Vars *v)
*
* Writes the received file data to the output file.
* If in ASCII mode, stops writing at first ^Z, and converts all
* \r\n pairs or solo \r's to \n's.
**********************************************************/
short putsec(struct Vars *v)
{
static char nl = '\n';
UBYTE *p;
short n;
/* If in binary mode, write it out verbatim */
if (v->Thisbinary) /* Ignore data received already */
{
if(v->Rxpos != v->Rxbytes)
{
v->xpru.xpru_updatemask = XPRU_ERRORMSG;
sprintf(v->Msgbuf,"Redundant Data ignored; Here=%ld, There=%ld",v->Rxbytes,v->Rxpos);
v->xpru.xpru_errormsg = (char *)v->Msgbuf;
(*v->io.xpr_update)(&v->xpru);
return OK;
}
if (bfwrite(v, v->Pktbuf, (long) v->Rxcount) != v->Rxcount) goto diskfull;
}
else
{
/* If in text mode, perform end-of-line cleanup */
if (v->Eofseen) return OK;
for (p = v->Pktbuf, n = v->Rxcount; --n >= 0; ++p)
{
if (*p == CPMEOF)
{
v->Eofseen = TRUE;
return OK;
}
else if (*p != '\n' && v->Lastsent == '\r')
{
if (bfwrite(v, &nl, 1L) != 1) goto diskfull;
}
if (*p != '\r' && bfwrite(v, p, 1L) != 1) goto diskfull;
v->Lastsent = *p;
}
}
/* Update comm program status display */
v->xpru.xpru_updatemask = XPRU_BLOCKS | XPRU_BLOCKSIZE | XPRU_BYTES
| XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE | XPRU_BLOCKCHECK;
++v->xpru.xpru_blocks;
v->xpru.xpru_blocksize = v->Rxcount;
v->xpru.xpru_bytes = v->Rxbytes += v->Rxcount;
v->xpru.xpru_blockcheck = v->Crc32 ? "CRC-32" : "CRC-16";
update_rate(v);
(*v->io.xpr_update)(&v->xpru);
v->Rxpos += v->Rxcount; /* update this pointer */
return OK;
diskfull:
upderr(v, "Error writing file; disk full?");
v->Noroom = TRUE;
return ERROR;
} /* End of short putsec() */
/**********************************************************
* void ackbibi(struct Vars *v)
*
* End of batch transmission; disengage cleanly from sender
**********************************************************/
void ackbibi(struct Vars *v)
{
stohdr(v, 0L);
/* just send a ZFIN and that's it... */
zshhdr(v,ZFIN);
sendbuf(v);
return;
} /* End of void ackbibi() */
/* End of Receive.c source */